package com.rao.mideng.core;

import com.alibaba.fastjson.JSON;

import com.rao.mideng.annotation.RaoMiDeng;
import com.rao.mideng.utils.HttpRequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 幂等处理拦截逻辑
 */
@Slf4j
public class MiDengInterceptor implements MethodInterceptor {

    private StringRedisTemplate stringRedisTemplate;
    private static final String pattern = "\"[A-Za-z0-9]+\":";
    private final String expireTime;

    public MiDengInterceptor(StringRedisTemplate stringRedisTemplate, String expireTime) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.expireTime = expireTime;
    }


    /**
     * 对key压缩，只要属性的value就ok了。简单来说不要存放那么长的key在redis里面去
     */
    private String generateSimpleKey(Object[] arguments) {
        return Arrays.stream(arguments)
                .map(JSON::toJSONString)
                .map(json -> json.replaceAll(pattern, ""))
                .collect(Collectors.joining());
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        HttpServletRequest request = HttpRequestUtil.httpServletRequest();
        //请求的实参
        Object[] arguments = invocation.getArguments();
        String simpleKey = generateSimpleKey(arguments);
        //请求的url
        String url = request.getRequestURI();
        RaoMiDeng annotation = invocation.getMethod().getAnnotation(RaoMiDeng.class);
        //请求前处理幂等操作
        String redisKey = simpleKey + url;
        return handlerAfter(invocation, redisKey);
    }

    private Object handlerAfter(MethodInvocation invocation, String redisKey) throws Throwable {
        Object proceed = invocation.proceed();
        if (stringRedisTemplate.opsForValue().setIfAbsent(redisKey, ""/*JSON.toJSONString(proceed)*/, 30, TimeUnit.SECONDS)) {
            return proceed;
        } else {
            //设置为事务为回滚状态
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return proceed;
            //说明有线程处理succeed，setnx失败的线程就要回滚。
//                throw new RaoMiDengException(annotation.message());
        }
//    }
//
//    /**
//     * todo 这个逻辑错了，后续在完成
//     */
//    @Deprecated
//    private Object handlerBefore(MethodInvocation invocation, RaoMiDeng annotation, String redisKey) throws Throwable {
//        String lockKey = redisKey + "lock";
//        //加锁成功的往下走
////            while (!stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "", annotation.time(), TimeUnit.SECONDS)) {
////                Thread.sleep(10);
////            }
//        //失败了直接不处理，这里要知道超时重试的间隔时间就好了，@value，不过一般超时重试好像是不会有多线程的问题
//        if (!stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "", annotation.time(), TimeUnit.SECONDS)) {
//            throw new RaoMiDengException(annotation.message());
//        }
//        Object proceed = invocation.proceed();
//        //keys
//        List<String> keys = new ArrayList<>();
//        keys.add(redisKey);
//        keys.add(lockKey);
//        //argv
//        List<String> params = new ArrayList<>();
////            params.add(JSON.toJSONString(proceed));
//        params.add("1");
//        //幂等key-30s过期
//        params.add(expireTime);
//        Object eval;
//        boolean flag = true;
//        try {
//            Jedis nativeConnection = (Jedis) stringRedisTemplate.getConnectionFactory().getConnection().getNativeConnection();
//            /**<? 拿到不为null的值，就可以抛异常回滚*/
//            eval = nativeConnection.eval(before_lua, keys, params);
//            flag = false;
//        } finally {
//            //虽然lua脚本有删除lock-key的，如果lua脚本执行正常的话，就无需再次del-lock-key
//            if (flag) {
//                stringRedisTemplate.delete(lockKey);
//            }
//        }
//        //1代表之前有人处理了，报异常，让事务回滚
//        if ("1".equals(eval)) {
//            throw new RaoMiDengException(annotation.message());
//        }
//        return proceed;
//    }
//    /**
//     * 开启前置幂等的话，执行前置幂等逻辑，默认是后置幂等
//     * 检索幂等key是否存在，存在的话【说明已经有线程做了处理这个请求】，返回1,不存在返回0
//     * 不存在的话，设置幂等key，设置过期时间，删除lock-key
//     */
//    private static final String before_lua =
//            "if redis.call(\"EXISTS\",KEYS[1]) == 1 then\n" +
//                    "        return  1\n" +
//                    "else\n" +
//                    "        redis.call(\"SET\",KEYS[1],ARGV[1])\n" +
//                    "        redis.call('EXPIRE',KEYS[1],ARGV[2])\n" +
//                    "        redis.call(\"DEL\",KEYS[2])\n" +
//                    "        return 0\n" +
//                    "end";
    }
}